package com.tes.util;

import java.rmi.RemoteException;
import java.util.List;

import javax.ejb.EJB;

import com.tes.VO.NodoVO;
import com.tes.VO.PriorizacionVO;
import com.tes.VO.RequerimientoVO;
import com.tes.common.ProyectoADMLocal;
import com.tes.entity.Priorizacion;
import com.tes.entity.Requerimiento;

public class BinarySearchTree {
	
	private NodoVO raiz;
	private static BinarySearchTree instancia = null;
		
	public static BinarySearchTree getInstance()
	{
		if(instancia==null)
		{
			instancia= new BinarySearchTree();
		}
		return instancia;
	}

    private BinarySearchTree() 
    {
    }
            
    public NodoVO generarBSTConListaObject (List<Object[]> priorizaciones) 
    {
    	this.raiz=null;
    	
    	for (Object[] prior : priorizaciones)
		{
			long valor = (Long)prior[0];
			Requerimiento req = (Requerimiento)prior[1];
			
			RequerimientoVO reqVO = new RequerimientoVO();
            reqVO.setIdRequerimiento(req.getIdRequerimiento());
            reqVO.setDescripcion(req.getDescripcion());
            reqVO.setNombre(req.getNombre());
            reqVO.setNroRequerimiento(req.getNroRequerimiento());
            
			generarBST(valor, reqVO);
        }
    	
    	return this.raiz;
    }
    
    public NodoVO generarBSTConListaPriorizaciones (List<Priorizacion> priorizaciones)
    {
    	this.raiz=null;
    	
    	for(Priorizacion prio:priorizaciones)
		{
    		long valor = (long)prio.getValor();
			Requerimiento req = prio.getRequerimiento();
			
			RequerimientoVO reqVO = new RequerimientoVO();
            reqVO.setIdRequerimiento(req.getIdRequerimiento());
            reqVO.setDescripcion(req.getDescripcion());
            reqVO.setNombre(req.getNombre());
            reqVO.setNroRequerimiento(req.getNroRequerimiento());
            
			generarBST(valor, reqVO);
        }
    	
    	return this.raiz;
    }

    private void generarBST(long valor, RequerimientoVO reqVO)
    {
    	NodoVO nodo = buscarNodo(valor);
        if (nodo == null)
        {
            NodoVO nuevo = new NodoVO(valor, null, null);
            nuevo.getRequerimiento().add(reqVO);
            
            this.raiz = insertarNodo(nuevo, this.raiz);
        }
        else
        {            
        	nodo.getRequerimiento().add(reqVO);
        }
            
	}

	private NodoVO insertarNodo(NodoVO nuevo, NodoVO subArbol)
	{
		if (subArbol == null)
        {
			subArbol = nuevo;
        }
        else if (nuevo.getValor() < subArbol.getValor())
        {
        	subArbol.setNodoIzq( insertarNodo(nuevo, subArbol.getNodoIzq()) );
        	 
        	if (obtenerAltura(subArbol.getNodoIzq()) - obtenerAltura(subArbol.getNodoDer()) == 2)
            {
                if (nuevo.getValor() < subArbol.getNodoIzq().getValor())
                {
                    subArbol = rotacionHijoIzquierdo(subArbol); /* Caso 1 */
                }
                else
                {
                    subArbol = rotacionDobleHijoIzquierda(subArbol); /* Caso 2 */
                }
            }
        }
		else
		{
			subArbol.setNodoDer( insertarNodo (nuevo, subArbol.getNodoDer()) );
			
			if (obtenerAltura(subArbol.getNodoDer()) - obtenerAltura(subArbol.getNodoIzq()) == 2)
            {
                if (nuevo.getValor() > subArbol.getNodoDer().getValor())
                {
                    subArbol = rotacionHijoDerecho(subArbol); /* Caso 4 */
                }
                else
                {
                    subArbol = rotacionDobleHijoDerecho(subArbol); /* Caso 3 */
                }
            }
        }
		
        int alturaSubArbol = max(obtenerAltura(subArbol.getNodoIzq()), obtenerAltura(subArbol.getNodoDer())) + 1;
        subArbol.setAltura(alturaSubArbol);
        
        return subArbol;
	}

	public NodoVO buscarNodo(long valor)
    {
    	boolean existe = false;
    	NodoVO nodo = null;
    	
        NodoVO reco = this.raiz;
        
        while (reco!=null && !existe)
        {
            if (valor == reco.getValor())
            {
                existe = true;
                nodo = reco;
            }
            else
            {
                if (valor > reco.getValor())
                {
                    reco=reco.getNodoDer();
                }
                else
                {
                    reco=reco.getNodoIzq();
                }
            }
        }
        return nodo;
    }
    
	private static int max(int alturaIzq, int alturaDer)
	{
		if (alturaIzq > alturaDer)
		{
			return alturaIzq;
		}
		else
		{
			return alturaDer;
		}
    }

    private static NodoVO rotacionHijoIzquierdo(NodoVO subArbol)
    {
        NodoVO aux2 = subArbol.getNodoIzq();
        subArbol.setNodoIzq(aux2.getNodoDer());
        aux2.setNodoDer(subArbol);
        
        int alturaSubArbol = max(obtenerAltura(subArbol.getNodoIzq()), obtenerAltura(subArbol.getNodoDer())) + 1;
        subArbol.setAltura(alturaSubArbol);
       
        alturaSubArbol = max(obtenerAltura(aux2.getNodoIzq()), obtenerAltura(subArbol)) + 1;
        aux2.setAltura(alturaSubArbol);
        
        return aux2;
   
    }

    private static NodoVO rotacionHijoDerecho(NodoVO subArbol)
    {
    	NodoVO aux2 = subArbol.getNodoDer();
    	subArbol.setNodoDer(aux2.getNodoIzq());
    	aux2.setNodoIzq(subArbol);
        
    	int alturaSubArbol = max(obtenerAltura(subArbol.getNodoIzq()), obtenerAltura(subArbol.getNodoDer())) + 1;
        subArbol.setAltura(alturaSubArbol);
       
        alturaSubArbol = max(obtenerAltura(aux2.getNodoDer()), obtenerAltura(subArbol)) + 1;
        aux2.setAltura(alturaSubArbol);
        
        return aux2;
    }
    
    private static NodoVO rotacionDobleHijoIzquierda(NodoVO subArbol)
    {
        subArbol.setNodoIzq( rotacionHijoDerecho(subArbol.getNodoIzq()) );
        return rotacionHijoIzquierdo(subArbol);
    }

    private static NodoVO rotacionDobleHijoDerecho(NodoVO subArbol)
    {
        subArbol.setNodoDer( rotacionHijoIzquierdo(subArbol.getNodoDer()) );
        return rotacionHijoDerecho(subArbol);
    }

    private static int obtenerAltura(NodoVO subArbol)
    {
    	if (subArbol == null)
    	{
    		return -1;
    	}
    	else
    	{
    		return subArbol.getAltura();
    	}
    }

    
    public void armarPriorizaciones (NodoVO reco, List<PriorizacionVO> priorizaciones)
    {
        if (reco != null)
        {
        	armarPriorizaciones(reco.getNodoDer(), priorizaciones);
        	
            for (RequerimientoVO rVO: reco.getRequerimiento())
            {
            	PriorizacionVO priorVO = new PriorizacionVO();
            	priorVO.setValor(reco.getValor());
            	priorVO.setRequerimiento(rVO);
            	
            	priorizaciones.add(priorVO);
            }
            
            armarPriorizaciones(reco.getNodoIzq(), priorizaciones);
        }
    }
}
